// 2006-4-4 create

TESTCASE(testNewFunction);
function testNewFunction() {
  var add = new Function("x", "y", "return(x+y)");
  assertEquals( "function", typeof add);
  assertEquals( 3, add(1, 2) );
  assertEquals( 3, add(1, 2, 3) );
  assertEquals( 5, add.call(1, 2, 3) );
  assertEquals( 5, add.apply(1, [2, 3]) );
  assertEquals( 5, add.apply(1, new Array(2, 3)) );

  add = function (x, y) { return x + y + 1; };
  assertEquals( 4, add(1, 2) );
}

TESTCASE(testArguments);
function testArguments(arg1, arg2) {
  assertEquals( 0, arguments.length );
  assertTrue( null == arg1 );
  assertEquals( "undefined", typeof arg1 );
  assertEquals( undefined, arguments[0] );
  // TODO: arguments.caller?
}

function FunctionTest_callMe() {
  if ("undefined" == typeof FunctionTest_callMe.caller) {
    fail("Error: function.caller property is not supported");
    return;
  }
  assertEquals( "function", typeof testCaller.caller );
  assertEquals( testCaller, FunctionTest_callMe.caller );
  assertEquals( FunctionTest_callMe, arguments.callee );
}

TESTCASE(testCaller);
function testCaller() {
  assertEquals( null, FunctionTest_callMe.caller );
  FunctionTest_callMe();
}

function FunctionTest_f1() { return 1; }

TESTCASE(testLocalFunction);
function testLocalFunction() {
  assertEquals( null, testCaller.caller );

  assertEquals( 2, FunctionTest_f1() );
  function FunctionTest_f1() { return 2; }
}

TESTCASE(testLocalFunction2);
function testLocalFunction2() {
  assertEquals( 1, FunctionTest_f1() );
}

function FunctionTestFunction1() {
  return this.n;
}

function FunctionTestFunction2(a, b) {
  return a + b + this.n;
}

TESTCASE(testApply);
function testApply() {
  assertEquals( "undefined", "" + FunctionTestFunction1() );
  var obj = { n: 9 };
  assertEquals( 9, FunctionTestFunction1.apply(obj) );

  assertEquals( "a undefined", FunctionTestFunction2("a", " ") );
  assertEquals( "a 9", FunctionTestFunction2.apply(obj, ["a", " "]) );

  assertEquals( "a 9", FunctionTestFunction2.call(obj, "a", " ") );
  assertEquals( "a,bundefined9", FunctionTestFunction2.call(obj, ["a", "b"]) );
}

function FunctionTestClosure1(a) {
  return function() { ++a; return a; };
}

TESTCASE(testClosure);
function testClosure() {
  var f = FunctionTestClosure1(5);
  assertEquals( 6, f() );
  assertEquals( 7, f() );

  var g = FunctionTestClosure1(10);
  assertEquals( 11, g() );
  assertEquals( 12, g() );

  assertEquals( 8, f() );

  assertTrue( f != g );
}

function getClosure(obj, func) {
  return function (arg0, arg1, arg2) { return func.apply(obj, [arg0, arg1, arg2]); };
}

TESTCASE(testGetClosure);
function testGetClosure() {
  var obj = { n: 8 };
  var f = getClosure(obj, FunctionTestFunction1);
  assertEquals( 8, f() );
}

function dumpCaller() {
  document.writeln(arguments.callee.caller);
}
dumpCaller();
(function (){
  dumpCaller();
})();

function dumpStackFunctionName() {
  var caller = arguments.callee.caller;
  var stackString = '';
  var count = 0;
  while (caller) {
    if (stackString)
      stackString += ' < ';
    var name = caller.name;
    if (!name) {
      name = '' + caller;
      if (name.length > 80) {
        var len = name.length;
        name = name.substring(0, 60) + '...' + name.substring(len - 20);
      }
    }
    stackString += name;
    if (++count > 50)
      break;
    caller = caller.caller;
  }
  return stackString;
}

TESTCASE(testPassByValue);
function testPassByValue() {

  function passByValue(obj) {
    obj = {};
    obj.n = 5;
  }

  var obj = {"n": "A"};
  assertEquals( "A", obj.n );
  passByValue(obj);
  assertEquals( "A", obj.n );
}

//---------------------------------------------------------------------------
if ("function" == typeof allTestsLocalRunner) allTestsLocalRunner();
